from cterm import *

class Symbol:
  def __init__(self, nr=-1, used=False, finalized=False):
    self.nr = nr
    self.used = used

class BreakLoop:
  pass

def trimSequence(sequence, availability):
  if checkSequence(sequence, availability):
    return sequence
  elif checkSequence(sequence[:-1]):
    return sequence[:-1]
  elif checkSequence(sequence[2:]):
    return sequence[2:]
  else:
    return trimSequence(sequence[2:-1])

def checkSequence(sequence, availability):
  if checkSequenceOnDataFlow(sequence) == False:
    return False

  if checkSequenceOnAvailability(sequence, availability) == False:
    return False

  return True

def mergeSymbols(s1, s2):
  for s in s2.keys():
    s1[s] = s2[s]

def checkSequenceOnDataFlow(sequence, symbols=None, nr=0):
  if symbols == None:
    symbols = dict()

  newseq = []

  opnum = -1
  for op in sequence:
    opnum += 1
    if opnum == len(sequence)-1:
      lastOp = True
    else:
      lastOp = False
    
    if op.id().name() == "_finalize":
      try:
        print "finalizing ", op.params().children[0]
        del symbols[op.params().children[0]]
      except:
        # Finalizing unknown variable
        return False
    elif op.id().name()[0:4] == "scOp":  
      newop = Funcall(op.id(), ATerm("[]", op.params().children[0:2]))
      
      for (t, p) in zip(None, op.params().children[0], op.params().children[2:-1]):
        print "checking ", t, p
        if t == 'i':
          if isinstance(p, FunCall):
            try:
              subsequence, subsymbols, nr = checkSequenceOnDataFlow([p], symbols, nr)
            except:
              # Subsequence doesn't check out
             return False

            symbols = mergeSymbols(symbols, subsymbols)
            newop.params().children.append(subsequence)
          else:
            if p in symbols:
              if symbols[p].used == True:
                # A stream may only be used once
                return False
              
              symbols[p].used=True

              # Find generator
              found = False
              try:
                for o in range(len(newseq)-1, -1, -1):
                  for a in range(len(newseq[o].params().children)-1, -1, -1):
                    if newseq[o].params().children[0][a] == 'I':
                      if newseq[o].params().children[2+a] == p:
                        newseq[o].params().children[2+a] = IntConst(symbols[p].nr)
                        raise BreakLoop
              except BreakLoop:
                found = True
                pass

              if found == False:
                # Couldn't find generator. Very strange.
                return False

              newop.params().children.append(IntConst(symbols[p].nr))
            else:
              # External input
              newop.params().children.append(p)

        elif t == 'I':
          # Output. Assume external; if it's internal, it will be changed by the
          # operation that uses it as input.
          symbols[p] = Symbol(nr)
          nr += 1
          newop.params().children.append(p)

        elif t == 'd' or t == 'f':
          if p in symbols:
            # Cannot pass scalars in a merged operation
            return False
          
          newop.params().children.append(p)
        elif t == 'D' or t == 'F':
          symbols[p] = Symbol(nr)
          newop.params().children.append(p)
        else:
          # Unknown type
          return False
          
      newseq += newop
    else:
      # Unknown function. This could be anything, so be cautious and reject
      # the sequence
      return False

  return newseq, symbols, nr

def checkSequenceOnAvailability(sequence, availability):
  procs = []
  first = True
  for op in sequence.children[0].children:
    nr = op.params().children[1].value()
    xmlopprocs = availability[nr].processors
    opprocs = []

    for proc in xmlopprocs:
      opprocs.append(proc.type)

    if first == False and procs != opprocs:
      return False
      
    procs = opprocs

  return True
